package com.rf.richfitwheel.common.aspect;


import com.rf.richfitwheel.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Objects;

@Slf4j
@Aspect
@Component
public class WebLogAspect {
    private static final String LINE_SEPARATOR = System.lineSeparator();

    /**
     * 声明切面pointcut为注解，及其对应id = logPointcut()
     */
    @Pointcut("@annotation(com.rf.richfitwheel.common.aspect.WebLog)")
    public void logPointcut() {}

    /**
     * 环绕通知
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around("logPointcut()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();

        Object[] args = pjp.getArgs();
        Object proceed = pjp.proceed(args);
        // pointcut method execution time
        log.info("API execution consumes {} ms", System.currentTimeMillis() - start);

        return proceed;
    }

    /**
     * 接入点joinPoint调用之前调用的方法
     * @param joinPoint
     */
    @Before("logPointcut()")
    public void doBefore(JoinPoint joinPoint) {
        // 获取request
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        // Async方法进来的话没有值，request不存在
        HttpServletRequest request = Objects.nonNull(attributes) ? attributes.getRequest() : null;

        // 获取@WebLog作用在JointPoint上的具体description信息
        String desc = getAspectDescription(joinPoint);

        log.info("============================= START ================================");
        // 请求地址
        log.info("API Request URL: {}", Objects.nonNull(request) ? request.getRequestURL().toString() : "");
        // 请求方法
        log.info("API Request Method: {}", Objects.nonNull(request) ? request.getMethod() : "");
        // 注解@WebLog作用在JointPoint上的具体description信息
        log.info("API description: {}", desc);
        // 请求调用的Service方法名
        log.info("API Service Method: {}", joinPoint.getSignature().getName());
        // 请求调用Service时传入的参数
//        if (joinPoint.getArgs() != null && joinPoint.getArgs().length != 0) {
//            final Object arg = joinPoint.getArgs()[0];
//            Object argPrintable = arg instanceof JSONObject ?
//                    (arg instanceof JSONArray ? JSONArray.fromObject(arg) : JSONObject.fromObject(arg)) :
//                    arg.toString();
//            log.info("API Request Parameters: {}", argPrintable);
//        }

        }

    /**
     * 接入点joinPoint调用后调用的方法
     * @param joinPoint
     */
    @After("logPointcut()")
    public void doAfter(JoinPoint joinPoint) {
        // 打印结束，换行
        log.info("============================ END ==================================" + LINE_SEPARATOR);
    }



    public String getAspectDescription(JoinPoint joinPoint) {
        StringBuilder description = null;
        try {
            // WebLog注解当前作用的方法所在的类canonical name完整名称，
            // 例如com.rf.richfitwheel.crmbusiness.service.impl.RiskMonitorServiceImpl
            String targetName = joinPoint.getTarget().getClass().getName();
            // WebLog注解当前作用的Pointcut Method名称，例如listRiskPrecautionByType
            String methodName = joinPoint.getSignature().getName();
            // WebLog注解当前作用的Pointcut Method的参数数组
            Object[] arguments = joinPoint.getArgs();

            // 为了获取WebLog注解当前作用的方法所在的类的所有的方法，用于和当前注解作用的方法名做比较
            Class targetClass = Class.forName(targetName);
            Method[] methods = targetClass.getMethods();

            description = new StringBuilder();

            // 如果方法名一致，且变量长度相同，则调用WebLog注解的description属性，作为调用API的描述信息
            for (Method method : methods) {
                if (method.getName().equals(methodName)) {
                    Class[] classes = method.getParameterTypes();
                    if (classes.length == arguments.length) {
                        description.append(method.getAnnotation(WebLog.class).description());
                        break;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return !StringUtils.isBlank(description) ? description.toString() : "";
    }

}
